﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Base
{
    /// <summary>
    /// 线程扩展类
    /// </summary>
    public sealed class ThreadEx : IThreadEx
    {
        /// <summary>
        /// 线程要执行的委托
        /// </summary>
        private readonly Action<ThreadExPara> _action = null;

        /// <summary>
        /// 线程名称
        /// </summary>
        private readonly string _name;

        /// <summary>
        /// 是否后台运行[true:后台线程;false:前台线程]
        /// </summary>
        private readonly bool _isBackground;

        /// <summary>
        /// 外部调用线程锁
        /// </summary>
        private readonly object _threadLock = new object();
        /// <summary>
        /// 执行线程
        /// </summary>
        private Thread _thread = null;
        /// <summary>
        /// 获取内部线程
        /// </summary>
        public Thread OwnerThread
        {
            get { return _thread; }
        }

        /// <summary>
        /// 当前线程是否正在运行[true:正在运行;false:未运行]
        /// </summary>
        private bool _runing = false;

        /// <summary>
        ///线程参数
        /// </summary>
        private ThreadStartPara _threadStartPara = null;

        private bool _disposed = false;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name">线程名称</param>
        /// <param name="isBackground">是否后台运行[true:后台线程;false:前台线程]</param>
        private ThreadEx(string name, bool isBackground)
        {
            this._name = name;
            this._isBackground = isBackground;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="action">线程要执行的委托</param>
        /// <param name="name">线程名称</param>
        /// <param name="isBackground">后台运行标识[true:后台线程;false:前台线程]</param>
        public ThreadEx(Action<ThreadExPara> action, string name = null, bool isBackground = true)
            : this(name, isBackground)
        {
            this._action = action ?? throw new ArgumentNullException(nameof(action));
        }





        #region IThreadEx
        /// <summary>
        /// 线程执行完成事件
        /// </summary>
        public event EventHandler<ThreadExCompletedArgs> Completed;

        /// <summary>
        /// 触发线程执行完成事件
        /// </summary>
        /// <param name="type">线程执行完成类型</param>
        /// <param name="ex">当执行异常可取消时可能的异常信息</param>
        private void OnRaiseCompleted(ThreadExCompletedType type, Exception ex = null)
        {
            var handler = this.Completed;
            handler?.Invoke(this, new ThreadExCompletedArgs(type, ex));
        }

        /// <summary>
        /// 获取线程当前的状态
        /// </summary>
        public System.Threading.ThreadState ThreadState
        {
            get
            {
                lock (this._threadLock)
                {
                    if (this._thread == null)
                    {
                        return System.Threading.ThreadState.Unstarted;
                    }
                    else
                    {
                        return this._thread.ThreadState;
                    }
                }
            }
        }

        /// <summary>
        /// 获取当前托管线程的唯一标识符
        /// </summary>
        public int ManagedThreadId
        {
            get
            {
                lock (this._threadLock)
                {
                    if (this._thread == null)
                    {
                        throw new Exception("线程未启动");
                    }
                    else
                    {
                        return this._thread.ManagedThreadId;
                    }
                }
            }
        }

        /// <summary>
        /// 当前线程是否正在运行
        /// </summary>
        public bool IsRuning
        {
            get
            {
                lock (this._threadLock)
                {
                    return this._runing;
                }
            }
        }

        /// <summary>
        /// 启动线程
        /// </summary>
        /// <param name="obj">线程启动参数</param>
        public void Start(object obj = null)
        {
            lock (this._threadLock)
            {
                if (this._disposed)
                {
                    throw new ObjectDisposedException(string.Empty, "对象已释放");
                }

                if (this._runing)
                {
                    return;
                }

                this._threadStartPara = new ThreadStartPara(obj);
                this._thread = new Thread(new ParameterizedThreadStart(this.ThreadExcuteMethod));

                if (string.IsNullOrWhiteSpace(this._name))
                {
                    var st = new System.Diagnostics.StackTrace(1, true);
                    var sf = st.GetFrame(0);
                    var method = sf.GetMethod();
                    this._thread.Name = string.Format("{0}.{1}.{2}.{3}", sf.GetFileName(), sf.GetFileLineNumber(), method.DeclaringType.FullName, method.Name);
                }
                else
                {
                    this._thread.Name = this._name;
                }

                this._thread.IsBackground = this._isBackground;
                this._runing = true;
                this._thread.Start(this._threadStartPara);
            }
        }

        /// <summary>
        /// 线程执行方法
        /// </summary>
        /// <param name="obj">线程参数</param>
        private void ThreadExcuteMethod(object obj)
        {
            var threadStartPara = (ThreadStartPara)obj;
            try
            {
                var token = threadStartPara.Cts.Token;
                this._action(new ThreadExPara(token, threadStartPara.Obj));
                if (token.IsCancellationRequested)
                {
                    this.OnRaiseCompleted(ThreadExCompletedType.Cancel);
                }
                else
                {
                    this.OnRaiseCompleted(ThreadExCompletedType.Completed);
                }
            }
            catch (System.Threading.ThreadAbortException taex)
            {
                this.OnRaiseCompleted(ThreadExCompletedType.Cancel, taex);
            }
            catch (Exception ex)
            {
                this.OnRaiseCompleted(ThreadExCompletedType.Exception, ex);
            }
            finally
            {
                this._runing = false;
            }
        }

        /// <summary>
        /// 停止线程
        /// </summary>
        public void Stop()
        {
            lock (this._threadLock)
            {
                if (this._runing)
                {
                    this._threadStartPara.Cancell();
                    this._threadStartPara.Dispose();
                    this._threadStartPara = null;
                    this._thread = null;
                    this._runing = false;
                }
            }
        }



        /// <summary>
        /// 停止线程
        /// </summary>
        /// <param name="timeoutMilliseconds">同步超时时间,-1表示无限期等待,单位/毫秒</param>
        /// <returns>如果线程已终止，则为 true；如果 false 参数指定的时间量已过之后还未终止线程，则为 timeout</returns>
        public bool SyncStop(int timeoutMilliseconds = -1)
        {
            bool result = true;
            lock (this._threadLock)
            {
                if (this._runing)
                {
                    this._threadStartPara.Cancell();
                    result = this._thread.Join(timeoutMilliseconds);

                    this._threadStartPara.Dispose();
                    this._threadStartPara = null;
                    this._thread = null;
                    this._runing = false;
                }
            }
            return result;
        }
        #endregion

        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            try
            {
                lock (this._threadLock)
                {
                    if (this._disposed)
                    {
                        return;
                    }
                    this._disposed = true;

                    if (this._runing)
                    {
                        this._threadStartPara.Cancell();
                        this._threadStartPara.Dispose();
                        this._threadStartPara = null;
                        this._thread = null;
                        this._runing = false;
                    }
                }
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }





        #region 静态方法
        /// <summary>
        /// 创建线程对象
        /// </summary>
        /// <param name="action">线程要执行的委托</param>
        /// <param name="name">线程名称</param>
        /// <param name="isBackground">是否后台运行[true:后台线程;false:前台线程]</param>
        /// <param name="obj">线程启动参数</param>
        /// <returns>返回线程对象</returns>
        public static IThreadEx Start(Action<ThreadExPara> action, string name = null, bool isBackground = true, object obj = null)
        {
            var thread = new ThreadEx(action, name, isBackground);
            thread.Start(obj);
            return thread;
        }


        /// <summary>
        /// 设置线程是否为后台线程
        /// </summary>
        /// <param name="thread">要设置的线程</param>
        /// <param name="isBackground">true:后台线程;false:前台线程</param>
        public static void SetThreadIsBackground(Thread thread, bool isBackground)
        {
            if (thread == null)
            {
                return;
            }

            thread.IsBackground = isBackground;
        }



        #region 精确到毫秒级的等待控制

        private static double _millisecondIterations = -1;
        /// <summary>
        /// 获取每毫秒CPU调度次数
        /// </summary>
        public static double MillisecondIterations
        {
            get { return _millisecondIterations; }
        }

        /// <summary>
        /// 设置每毫秒CPU调度次数
        /// </summary>
        /// <param name="millisecondIterations">每毫秒CPU调度次数,小于0自动计算</param>
        public static void SetIterations(int millisecondIterations)
        {
            if (millisecondIterations > 0)
            {
                _millisecondIterations = millisecondIterations;
                return;
            }

            InitIterations();
        }

        /// <summary>
        /// 设置每毫秒CPU调度次数
        /// </summary>
        /// <param name="caculatCount">计算次数,值越大越精确</param>
        /// <param name="sampleCount">每次计算采样次数,值越大越精确</param>
        public static void InitIterations(int caculatCount = 10, int sampleCount = 100)
        {
            List<decimal> srcList = new List<decimal>();
            var stopwatch = Stopwatch.StartNew();
            for (int i = 0; i < caculatCount; i++)
            {
                Thread.SpinWait(int.MaxValue);
                stopwatch.Stop();
                srcList.Add((((decimal)int.MaxValue) / ((decimal)stopwatch.Elapsed.TotalMilliseconds)));
            }

            //平均CPU调度int.MaxValue次时,耗费的毫秒数
            decimal intMaxValueIterations = srcList.Average();

            var list = new List<decimal>();
            int iterations = (int)intMaxValueIterations;

            for (int i = 0; i < sampleCount; i++)
            {
                stopwatch.Restart();
                Thread.SpinWait(iterations);
                stopwatch.Stop();
                list.Add((decimal)stopwatch.Elapsed.TotalMilliseconds);
            }

            decimal avg = list.Average();
            _millisecondIterations = (double)(intMaxValueIterations / avg);
        }


        /// <summary>
        /// CPU自旋等待指定毫秒数
        /// </summary>
        /// <param name="milliseconds">毫秒数</param>
        /// <returns>实际等待的毫秒数</returns>
        public static double SpinWait(double milliseconds)
        {
            Stopwatch stopwatch = Stopwatch.StartNew();
            double totalIterations = (_millisecondIterations * milliseconds);
            int mod;
            if (totalIterations - int.MaxValue > 0.1d)
            {
                int count = (int)(totalIterations / int.MaxValue);
                mod = (int)Math.Floor(totalIterations % int.MaxValue);

                for (int i = 0; i < count; i++)
                {
                    Thread.SpinWait(int.MaxValue);
                    mod--;
                }

                if (mod > 0)
                {
                    Thread.SpinWait(mod);
                }
            }
            else
            {
                mod = (int)Math.Floor(totalIterations);
                Thread.SpinWait(mod);
            }

            stopwatch.Stop();

            return stopwatch.Elapsed.TotalMilliseconds;
        }
        #endregion

        #endregion






    }

}
